import type { App } from 'vue'


/**
 * @description 设置样式
 * @param {object} object 样式对象
 * @returns {string} 返回样式字符串
 */
function setConvertStyle(object: { [key: string]: unknown }) {
  let style = ''
  for (const index in object) {
    /* eslint-disable-next-line no-prototype-builtins */
    if (object.hasOwnProperty(index)) {
      style += `${index}:${object[index]};`
    }
  }
  return style
}

/**
 * 按钮波浪指令
 * @param {App} app app实例
 * @example 默认方式：v-waves，如 `<div v-waves></div>`
 * @example 参数方式：v-waves=" |light|red|orange|purple|green|teal"，如 `<div v-waves="'light'"></div>`
 */
export function wavesDirective(app: App) {
  app.directive('waves', {
    mounted(element, binding) {
      element.classList.add('waves-effect')
      binding.value && element.classList.add(`waves-${binding.value}`)
      /**
       * 处理当前点击事件
       * @param {MouseEvent} event_ - 鼠标事件对象
       */
      function onCurrentClick(event_: { [key: string]: unknown }) {
        const elementDiv = document.createElement('div')
        elementDiv.classList.add('waves-ripple')
        element.append(elementDiv)
        const styles = {
          left: `${event_.layerX}px`,
          top: `${event_.layerY}px`,
          opacity: 1,
          transform: `scale(${(element.clientWidth / 100) * 10})`,
          'transition-duration': '750ms',
          'transition-timing-function':
            'cubic-bezier(0.250, 0.460, 0.450, 0.940)'
        }
        elementDiv.setAttribute('style', setConvertStyle(styles))
        setTimeout(() => {
          elementDiv.setAttribute(
            'style',
            setConvertStyle({
              opacity: 0,
              transform: styles.transform,
              left: styles.left,
              top: styles.top
            })
          )
          setTimeout(() => {
            elementDiv && elementDiv.remove()
          }, 750)
        }, 450)
      }
      element.addEventListener('mousedown', onCurrentClick, false)
    },
    unmounted(element) {
      element.addEventListener('mousedown', () => {})
    }
  })
}

/**
 * 自定义拖动指令
 * @param {App} app app实例
 * @example  使用方式：v-drag="[dragDom,dragHeader]"，如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
 * @example dragDom 要拖动的元素，dragHeader 要拖动的 Header 位置
 * @see https://github.com/element-plus/element-plus/issues/522 参考
 * @see https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242 注意
 */
export function dragDirective(app: App) {
  app.directive('drag', {
    mounted(element, binding) {
      if (!binding.value) {
        return false
      }

      const dragDom = document.querySelector(binding.value[0]) as HTMLElement
      const dragHeader = document.querySelector(binding.value[1]) as HTMLElement

      dragHeader.addEventListener('mouseover', () => (dragHeader.style.cursor = 'move'))

      /**
       * @description 鼠标按下事件
       * @param event_ 鼠标事件
       * @param type 类型
       * @returns 返回值
       */
      function down(event_: any, type: string) {
        // 鼠标按下，计算当前元素距离可视区的距离
        const disX =
          type === 'pc' ?
            event_.clientX - dragHeader.offsetLeft :
            event_.touches[0].clientX - dragHeader.offsetLeft
        const disY =
          type === 'pc' ?
            event_.clientY - dragHeader.offsetTop :
            event_.touches[0].clientY - dragHeader.offsetTop

        // body当前宽度
        const screenWidth = document.body.clientWidth
        // 可见区域高度(应为body高度，可某些环境下无法获取)
        const screenHeight = document.documentElement.clientHeight

        // 对话框宽度
        const dragDomWidth = dragDom.offsetWidth
        // 对话框高度
        const dragDomheight = dragDom.offsetHeight

        const minDragDomLeft = dragDom.offsetLeft
        const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth

        const minDragDomTop = dragDom.offsetTop
        const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight

        // 获取到的值带px 正则匹配替换
        let styL: any = getComputedStyle(dragDom).left
        let styT: any = getComputedStyle(dragDom).top

        // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
        if (styL.includes('%')) {
          styL = +document.body.clientWidth * (+styL.replaceAll('%', '') / 100)
          styT = +document.body.clientHeight * (+styT.replaceAll('%', '') / 100)
        } else {
          styL = +styL.replaceAll('px', '')
          styT = +styT.replaceAll('px', '')
        }

        return {
          disX,
          disY,
          minDragDomLeft,
          maxDragDomLeft,
          minDragDomTop,
          maxDragDomTop,
          styL,
          styT
        }
      }

      /**
       * @description 鼠标移动事件
       * @param event_ 鼠标事件
       * @param type 类型
       * @param object 对象
       */
      function move(event_: any, type: string, object: any) {
        const {
          disX,
          disY,
          minDragDomLeft,
          maxDragDomLeft,
          minDragDomTop,
          maxDragDomTop,
          styL,
          styT
        } = object

        // 通过事件委托，计算移动的距离
        let left =
          type === 'pc' ? event_.clientX - disX : event_.touches[0].clientX - disX
        let top = type === 'pc' ? event_.clientY - disY : event_.touches[0].clientY - disY

        // 边界处理
        if (-left > minDragDomLeft) {
          left = -minDragDomLeft
        } else if (left > maxDragDomLeft) {
          left = maxDragDomLeft
        }

        if (-top > minDragDomTop) {
          top = -minDragDomTop
        } else if (top > maxDragDomTop) {
          top = maxDragDomTop
        }

        // 移动当前元素
        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
      }

      /**
       * pc端
       * onmousedown 鼠标按下触发事件
       * onmousemove 鼠标按下时持续触发事件
       * onmouseup 鼠标抬起触发事件
       * @param {MouseEvent} e 鼠标事件
       */
      dragHeader.addEventListener('mousedown', event_ => {
        const object = down(event_, 'pc')
        document.addEventListener('mousemove', mouseEvent_ => {
          move(mouseEvent_, 'pc', object)
        })
        document.addEventListener('mouseup', () => {
          // eslint-disable-next-line unicorn/prefer-add-event-listener
          document.onmousemove = null
          // eslint-disable-next-line unicorn/prefer-add-event-listener
          document.onmouseup = null
        })
      })

      /**
       * 移动端
       * ontouchstart 当按下手指时，触发ontouchstart
       * ontouchmove 当移动手指时，触发ontouchmove
       * ontouchend 当移走手指时，触发ontouchend
       * @param {MouseEvent} e 鼠标事件
       */
      dragHeader.addEventListener('touchstart', touchStartEvent_ => {
        const object = down(touchStartEvent_, 'app')
        document.addEventListener('touchmove', touchMoveEvent_ => {
          move(touchMoveEvent_, 'app', object)
        })
        document.addEventListener('touchend', () => {
          // eslint-disable-next-line unicorn/prefer-add-event-listener
          document.ontouchmove = null
          // eslint-disable-next-line unicorn/prefer-add-event-listener
          document.ontouchend = null
        })
      })
    }
  })
}
